// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

use crate::{
    http::{policies::RetryHeaders, StatusCode},
    time::Duration,
};

/// Retry policy with a fixed back-off.
///
/// Retry policy with fixed back-off (with an added random delay up to 256 ms). Each retry will
/// happen at least after the same, configured sleep time. The policy will retry until the maximum number of
/// retries have been reached or the maximum allowed delay has passed (whichever comes first). The
/// wait time is not precise.
#[derive(Debug, Clone, PartialEq, Eq)]
pub(crate) struct FixedRetryPolicy {
    delay: Duration,
    max_retries: u32,
    max_elapsed: Duration,
    retry_headers: RetryHeaders,
    retry_status_codes: Vec<StatusCode>,
}

impl FixedRetryPolicy {
    pub(crate) fn new(
        delay: Duration,
        max_retries: u32,
        max_elapsed: Duration,
        retry_headers: RetryHeaders,
        retry_status_codes: Vec<StatusCode>,
    ) -> Self {
        Self {
            delay: delay.max(Duration::milliseconds(10)),
            max_retries,
            max_elapsed,
            retry_headers,
            retry_status_codes,
        }
    }
}

impl super::RetryPolicy for FixedRetryPolicy {
    fn is_expired(&self, time_since_start: Duration, retry_count: u32) -> bool {
        retry_count >= self.max_retries || time_since_start >= self.max_elapsed
    }

    fn retry_headers(&self) -> Option<&RetryHeaders> {
        Some(&self.retry_headers)
    }

    fn retry_status_codes(&self) -> &[StatusCode] {
        &self.retry_status_codes
    }

    fn sleep_duration(&self, _retry_count: u32) -> Duration {
        let sleep_ms = self.delay.whole_milliseconds() as u64 + u64::from(rand::random::<u8>());
        Duration::milliseconds(sleep_ms as i64)
    }
}
